package com.frejoys.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.frejoys.common.enums.EArea;
import com.frejoys.common.vo.common.AdcodeVo;
import com.frejoys.common.vo.common.AreaItemVo;
import com.frejoys.dao.entity.Area;
import com.frejoys.dao.mapper.AreaMapper;
import com.frejoys.service.AreaService;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

// 行政区域
@Service
@RequiredArgsConstructor
public class AreaServiceImpl extends ServiceImpl<AreaMapper, Area> implements AreaService {


    @Override
    @Cacheable(cacheNames = "area", key = "#root.method.name", unless = "#result.size() == 0")
    public List<Area> getProvince() {
        LambdaQueryWrapper<Area> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Area::getLevel, EArea.province.getValue());
        return list(wrapper);
    }

    @Override
    @Cacheable(cacheNames = "area", key = "#root.method.name", unless = "#result.size() == 0")
    public List<Area> getCity() {
        LambdaQueryWrapper<Area> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Area::getLevel, EArea.city.getValue());
        return list(wrapper);
    }

    @Override
    @Cacheable(cacheNames = "area", key = "#root.method.name", unless = "#result.size() == 0")
    public List<Area> getDistrict() {
        LambdaQueryWrapper<Area> wrapper = new LambdaQueryWrapper<>();
        wrapper.gt(Area::getLevel, EArea.city.getValue());
        return list(wrapper);
    }

    @Override
    @Cacheable(cacheNames = "area", key = "#root.method.name + '::' + #adcode", unless = "#result.size() == 0")
    public List<Area> getCity(String adcode) {
        LambdaQueryWrapper<Area> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Area::getParentAdcode, adcode).eq(Area::getLevel, EArea.city.getValue());
        return list(wrapper);
    }

    @Override
    @Cacheable(cacheNames = "area", key = "#root.method.name + '::' + #adcode", unless = "#result.size() == 0")
    public List<Area> getDistrict(String adcode) {
        LambdaQueryWrapper<Area> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Area::getParentAdcode, adcode).eq(Area::getLevel, EArea.district.getValue());
        return list(wrapper);
    }

    @Override
    @Cacheable(cacheNames = "area", key = "#root.method.name", unless = "#result == null")
    public Map<String, String> getNameMap() {
        LambdaQueryWrapper<Area> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(Area::getAdcode, Area::getName).lt(Area::getLevel, EArea.street.getValue());
        List<Area> list = list(wrapper);
        return list.stream().collect(Collectors.toMap(Area::getAdcode, Area::getName));
    }

    @Override
    @Cacheable(cacheNames = "area", key = "#root.method.name", unless = "#result.size() == 0")
    public List<AreaItemVo> getDisArea() {
        List<AreaItemVo> itemVos = new ArrayList<>();

        List<Area> city = getCity();

        // 市
        Map<String, List<Area>> cityMap = new HashMap<>();
        city.forEach( e -> {

            List<Area> areaVoList = cityMap.get(e.getParentAdcode());
            if (areaVoList == null) {
                areaVoList = new ArrayList<>();
            }

            areaVoList.add(e);

            cityMap.put(e.getParentAdcode(), areaVoList);
        });

        List<Area> district = getDistrict();

        // 区县
        Map<String, List<Area>> districtMap = new HashMap<>();
        district.forEach( e -> {

            List<Area> areaVoList = districtMap.get(e.getParentAdcode());
            if (areaVoList == null) {
                areaVoList = new ArrayList<>();
            }

            areaVoList.add(e);

            districtMap.put(e.getParentAdcode(), areaVoList);
        });

        List<Area> province = getProvince();

        province.forEach( e -> {

            List<AreaItemVo> itemVo = new ArrayList<>();

            // 市
            List<Area> cityList = cityMap.get(e.getAdcode());
            if (cityList != null) {
                cityList.forEach( c -> {
                    List<AreaItemVo> itemCityVo = new ArrayList<>();

                    // 区县
                    List<Area> districtList = districtMap.get(c.getAdcode());
                    if (districtList != null) {
                        districtList.forEach( d -> {
                            itemCityVo.add(getAreaItemData(d.getName(), d.getAdcode(), new ArrayList<>()));
                        });
                    }

                    itemVo.add(getAreaItemData(c.getName(), c.getAdcode(), itemCityVo));
                });
            }

            itemVos.add(getAreaItemData(e.getName(), e.getAdcode(), itemVo));
        });

        return itemVos;
    }

    @Override
    public AreaItemVo getAreaItemData(String text, String value, List<AreaItemVo> children) {
        AreaItemVo pVo = new AreaItemVo();
        pVo.setText(text);
        pVo.setValue(value);
        pVo.setChildren(children);
        return pVo;
    }

    @Override
    public AdcodeVo getProvinceCityDistrict(String district) {
        LambdaQueryWrapper<Area> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(Area::getAdcode, Area::getParentAdcode).eq(Area::getAdcode, district).eq(Area::getLevel, EArea.district.getValue());
        Area area = getOnly(wrapper);
        if (area == null) {
            return null;
        }

        // 获取省
        LambdaQueryWrapper<Area> wrapper1 = new LambdaQueryWrapper<>();
        wrapper1.eq(Area::getAdcode, area.getParentAdcode()).eq(Area::getLevel, EArea.city.getValue());
        Area area1 = getOnly(wrapper1);
        if (area1 == null) {
            return null;
        }

        AdcodeVo vo = new AdcodeVo();
        vo.setProvince(area1.getParentAdcode());
        vo.setCity(area1.getAdcode());
        vo.setDistrict(district);
        return vo;
    }

}